package com.navinfo.tripanalysis.common.arithmetic;

import com.navinfo.tripanalysis.common.arithmetic.common.*;
import com.navinfo.tripanalysis.common.arithmetic.custom.VarGear;
import com.navinfo.tripanalysis.common.arithmetic.manager.AbstractArithmetic;
import com.navinfo.tripanalysis.common.config.ConfigMap;
import com.navinfo.tripanalysis.common.arithmetic.common.VehicleInfo;
import com.navinfo.tripanalysis.common.protocol.java.EventDataPb;
import com.navinfo.tripanalysis.common.util.ArithmeticUtils;

import java.math.BigDecimal;

/**
 * 挡位统计算法<br/>
 * 依赖{@link Mileage}里程油耗算法<br/>
 * 赋值{@link CommonData#gear}的档位<br/>
 * 输出：统计、事件<br/>
 */
public class Gear extends AbstractArithmetic<VarGear> {
    /**
     * 速度阈值
     */
    private int gearSpeedThreshold = ConfigMap.getInteger("Gear_Speed_Threshold");
    /**
     * 挡位阈值
     */
    private double gearThresholdMin = ConfigMap.getDouble("Gear_Threshold");
    /**
     * 挡位经验值
     */
    private double gearCoefficient = ConfigMap.getDouble("Gear_Coefficient");
    /**
     * 转数阈值
     */
    private int gearRotationThreshold = ConfigMap.getInteger("Gear_Rotation_Threshold");
    /**
     * 油门开度阈值
     */
    private int gearAcceleratorThreshold = ConfigMap.getInteger("Gear_Accelerator_Threshold");
    /**
     * 滤波系数
     */
    private double gearFilterCoefficient = ConfigMap.getDouble("Gear_Filter_Coefficient");
    /**
     * 干扰值过滤点数
     */
    private int gearInterfereCount = ConfigMap.getInteger("Gear_Interfere_Count");


    @Override
    public void calculate() {
        logger.debug("===============档位算法开始===========");
        //获取车辆配置信息
        VehicleInfo vehicleInfo = drivingData.getVehicleInfo();
        if (vehicleInfo == null) {
            logger.debug("挡位算法：获取车辆信息为空，返回");
            return;
        }

        //获取各档位速比
        double[] gearsSpeedRatio = vehicleInfo.getGearsSpeedRatio();
        if (gearsSpeedRatio == null || gearsSpeedRatio.length == 0) {
            logger.debug("挡位算法：变速箱速比为null，返回");
            return;
        }

        //获取车轮半径
        double wheelRadius = vehicleInfo.getWheelRadius();
        if(wheelRadius == 0){
            logger.debug("挡位算法：车轮半径为0不计算，返回");
            return;
        }

        //获取后桥速比
        double rearAxleSpeedRatio = vehicleInfo.getRearAxleSpeedRatio();
        if(rearAxleSpeedRatio == 0){
            logger.debug("挡位算法：后桥速比为0不计算，返回");
            return;
        }

        VarGear varGear = getObj();
        CommonData commonData = getCommonData();

        //如果速度小于阈值不计算
        if (commonData.getSpeedMs().compareTo(BigDecimal.valueOf(gearSpeedThreshold)) <= 0) {
            logger.debug("挡位算法：速度小于阈值不计算，返回");
            return;
        }

        int rotation = commonData.getRotation();
        int speedKmh = commonData.getSpeedKmh();
        double accelerator = commonData.getAccelerator();

        //档位默认为空挡99
        int curGear = 99;
        //如果rotation<=750&&油门开度=0，计为空挡，统计到其它挡位中
        if (!(rotation<=gearRotationThreshold && (BigDecimal.valueOf(accelerator).compareTo(BigDecimal.valueOf(gearAcceleratorThreshold))== 0)) ) {
            double m = rotation * 2 * Math.PI * wheelRadius * 60;
            logger.debug("rotation:{},wheelRadius:{},rotation*2*Math.PI*wheelRadius:{}", rotation, wheelRadius, m);

            double n = speedKmh * rearAxleSpeedRatio * 1000;
            logger.debug("speedKmh:{},rearAxleSpeedRatio:{},speedKmh*rearAxleSpeedRatio*0.06:{}", speedKmh, rearAxleSpeedRatio, n);

            //计算档位
            if (n != 0) {
                double gear = BigDecimal.valueOf(m).divide(BigDecimal.valueOf(n), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
                if (commonData.isTripFirstNode()) {
                    varGear.setFiltered(gear);
                } else {
                    varGear.setFiltered(varGear.getFiltered() + gearFilterCoefficient *(gear- varGear.getFiltered()));
                }
                double filterGear = varGear.getFiltered();
                logger.debug("gear:{},filterGear:{}", gear, filterGear);

                double min = gearThresholdMin;
                for (int i=0; i<gearsSpeedRatio.length; i++) {
                    double delta = Math.abs(gearsSpeedRatio[i] - filterGear);
                    if (delta < min) {
                        curGear = i;
                        min = delta;
                    }
                }
                logger.debug("去抖前index:{}", curGear);

                //如果计算的速比与最接近的变速箱速比差值大于阈值，记为空挡
                double diffGearsSpeedRatio = Math.abs(gearsSpeedRatio[curGear] - filterGear);
                logger.debug("diffGearsSpeedRatio:{},gearCoefficient:{}", diffGearsSpeedRatio, gearsSpeedRatio);
                if (BigDecimal.valueOf(diffGearsSpeedRatio).compareTo(BigDecimal.valueOf(gearCoefficient)) > 0) {
                    curGear = 99;
                }
                logger.debug("去抖后index:{}", curGear);
            }
        }

        //计算档位的统计数据和事件数据
        calcGearData(curGear);

        //赋值到公共参数中，供其他算法使用
        commonData.setGear(curGear);

        //设置上一个点状态、供下个点计算
        varGear.setPreRotation(commonData.getRotation());
        varGear.setPreLatitude(commonData.getLatitude());
        varGear.setPreLongitude(commonData.getLongitude());
        varGear.setPreHeight(commonData.getHeight());
        varGear.setPreGpsTime(commonData.getGpsTime());
        varGear.setPreSpeedKmh(commonData.getSpeedKmh());
        varGear.setPreGpsDirection(commonData.getGpsDirection());
        logger.debug("===============档位算法结束===========");
    }

    @Override
    public void collect(OuterStatisticData statisticDataOut) {
        VarGear var = getObj();
        statisticDataOut.setShiftGearsNumber(var.getShiftGearNumber());

        VarCommonNumber gear01 = var.getGear01();
        statisticDataOut.setGear01Number(gear01.getNumber());
        statisticDataOut.setGear01Fuel(ArithmeticUtils.fuelMlToMl(gear01.getFuel()));
        statisticDataOut.setGear01Mileage(ArithmeticUtils.mileageMToM(gear01.getMileage()));
        statisticDataOut.setGear01Duration(ArithmeticUtils.timeMsToSecond(gear01.getDuration()));

        VarCommonNumber gear02 = var.getGear02();
        statisticDataOut.setGear02Number(gear02.getNumber());
        statisticDataOut.setGear02Fuel(ArithmeticUtils.fuelMlToMl(gear02.getFuel()));
        statisticDataOut.setGear02Mileage(ArithmeticUtils.mileageMToM(gear02.getMileage()));
        statisticDataOut.setGear02Duration(ArithmeticUtils.timeMsToSecond(gear02.getDuration()));

        VarCommonNumber gear03 = var.getGear03();
        statisticDataOut.setGear03Number(gear03.getNumber());
        statisticDataOut.setGear03Fuel(ArithmeticUtils.fuelMlToMl(gear03.getFuel()));
        statisticDataOut.setGear03Mileage(ArithmeticUtils.mileageMToM(gear03.getMileage()));
        statisticDataOut.setGear03Duration(ArithmeticUtils.timeMsToSecond(gear03.getDuration()));

        VarCommonNumber gear04 = var.getGear04();
        statisticDataOut.setGear04Number(gear04.getNumber());
        statisticDataOut.setGear04Fuel(ArithmeticUtils.fuelMlToMl(gear04.getFuel()));
        statisticDataOut.setGear04Mileage(ArithmeticUtils.mileageMToM(gear04.getMileage()));
        statisticDataOut.setGear04Duration(ArithmeticUtils.timeMsToSecond(gear04.getDuration()));

        VarCommonNumber gear05 = var.getGear05();
        statisticDataOut.setGear05Number(gear05.getNumber());
        statisticDataOut.setGear05Fuel(ArithmeticUtils.fuelMlToMl(gear05.getFuel()));
        statisticDataOut.setGear05Mileage(ArithmeticUtils.mileageMToM(gear05.getMileage()));
        statisticDataOut.setGear05Duration(ArithmeticUtils.timeMsToSecond(gear05.getDuration()));

        VarCommonNumber gear06 = var.getGear06();
        statisticDataOut.setGear06Number(gear06.getNumber());
        statisticDataOut.setGear06Fuel(ArithmeticUtils.fuelMlToMl(gear06.getFuel()));
        statisticDataOut.setGear06Mileage(ArithmeticUtils.mileageMToM(gear06.getMileage()));
        statisticDataOut.setGear06Duration(ArithmeticUtils.timeMsToSecond(gear06.getDuration()));

        VarCommonNumber gear07 = var.getGear07();
        statisticDataOut.setGear07Number(gear07.getNumber());
        statisticDataOut.setGear07Fuel(ArithmeticUtils.fuelMlToMl(gear07.getFuel()));
        statisticDataOut.setGear07Mileage(ArithmeticUtils.mileageMToM(gear07.getMileage()));
        statisticDataOut.setGear07Duration(ArithmeticUtils.timeMsToSecond(gear07.getDuration()));

        VarCommonNumber gear08 = var.getGear08();
        statisticDataOut.setGear08Number(gear08.getNumber());
        statisticDataOut.setGear08Fuel(ArithmeticUtils.fuelMlToMl(gear08.getFuel()));
        statisticDataOut.setGear08Mileage(ArithmeticUtils.mileageMToM(gear08.getMileage()));
        statisticDataOut.setGear08Duration(ArithmeticUtils.timeMsToSecond(gear08.getDuration()));

        VarCommonNumber gear09 = var.getGear09();
        statisticDataOut.setGear09Number(gear09.getNumber());
        statisticDataOut.setGear09Fuel(ArithmeticUtils.fuelMlToMl(gear09.getFuel()));
        statisticDataOut.setGear09Mileage(ArithmeticUtils.mileageMToM(gear09.getMileage()));
        statisticDataOut.setGear09Duration(ArithmeticUtils.timeMsToSecond(gear09.getDuration()));

        VarCommonNumber gear10 = var.getGear10();
        statisticDataOut.setGear10Number(gear10.getNumber());
        statisticDataOut.setGear10Fuel(ArithmeticUtils.fuelMlToMl(gear10.getFuel()));
        statisticDataOut.setGear10Mileage(ArithmeticUtils.mileageMToM(gear10.getMileage()));
        statisticDataOut.setGear10Duration(ArithmeticUtils.timeMsToSecond(gear10.getDuration()));

        VarCommonNumber gear11 = var.getGear11();
        statisticDataOut.setGear11Number(gear11.getNumber());
        statisticDataOut.setGear11Fuel(ArithmeticUtils.fuelMlToMl(gear11.getFuel()));
        statisticDataOut.setGear11Mileage(ArithmeticUtils.mileageMToM(gear11.getMileage()));
        statisticDataOut.setGear11Duration(ArithmeticUtils.timeMsToSecond(gear11.getDuration()));

        VarCommonNumber gear12 = var.getGear12();
        statisticDataOut.setGear12Number(gear12.getNumber());
        statisticDataOut.setGear12Fuel(ArithmeticUtils.fuelMlToMl(gear12.getFuel()));
        statisticDataOut.setGear12Mileage(ArithmeticUtils.mileageMToM(gear12.getMileage()));
        statisticDataOut.setGear12Duration(ArithmeticUtils.timeMsToSecond(gear12.getDuration()));

        VarCommonNumber gear13 = var.getGear13();
        statisticDataOut.setGear13Number(gear13.getNumber());
        statisticDataOut.setGear13Fuel(ArithmeticUtils.fuelMlToMl(gear13.getFuel()));
        statisticDataOut.setGear13Mileage(ArithmeticUtils.mileageMToM(gear13.getMileage()));
        statisticDataOut.setGear13Duration(ArithmeticUtils.timeMsToSecond(gear13.getDuration()));

        VarCommonNumber gear14 = var.getGear14();
        statisticDataOut.setGear14Number(gear14.getNumber());
        statisticDataOut.setGear14Fuel(ArithmeticUtils.fuelMlToMl(gear14.getFuel()));
        statisticDataOut.setGear14Mileage(ArithmeticUtils.mileageMToM(gear14.getMileage()));
        statisticDataOut.setGear14Duration(ArithmeticUtils.timeMsToSecond(gear14.getDuration()));

        VarCommonNumber gear15 = var.getGear15();
        statisticDataOut.setGear15Number(gear15.getNumber());
        statisticDataOut.setGear15Fuel(ArithmeticUtils.fuelMlToMl(gear15.getFuel()));
        statisticDataOut.setGear15Mileage(ArithmeticUtils.mileageMToM(gear15.getMileage()));
        statisticDataOut.setGear15Duration(ArithmeticUtils.timeMsToSecond(gear15.getDuration()));

        VarCommonNumber gear16 = var.getGear16();
        statisticDataOut.setGear16Number(gear16.getNumber());
        statisticDataOut.setGear16Fuel(ArithmeticUtils.fuelMlToMl(gear16.getFuel()));
        statisticDataOut.setGear16Mileage(ArithmeticUtils.mileageMToM(gear16.getMileage()));
        statisticDataOut.setGear16Duration(ArithmeticUtils.timeMsToSecond(gear16.getDuration()));

        VarCommonNumber gearOther = var.getGearOther();
        statisticDataOut.setGearOtherNumber(gearOther.getNumber());
        statisticDataOut.setGearOtherFuel( ArithmeticUtils.fuelMlToMl(gearOther.getFuel()));
        statisticDataOut.setGearOtherMileage(ArithmeticUtils.mileageMToM(gearOther.getMileage()));
        statisticDataOut.setGearOtherDuration(ArithmeticUtils.timeMsToSecond(gearOther.getDuration()));
    }

    /**
     * 计算挡位的统计和事件指标
     * @param curGear ,当前档位
     */
    private void calcGearData(int curGear) {
        VarGear var = getObj();
        CommonData commonData = getCommonData();

        //设置持续档位相关参数
        if(curGear == var.getHoldGear()){
            if(var.getHoldCount() > gearInterfereCount){
                var.setPreGear(curGear);
            }
            var.setHoldCount(var.getHoldCount() +1);
        }else{
            var.setHoldCount(0);
            var.setHoldGear(curGear);
        }

        //与上一点时间差、里程差、油耗差
        int dffTime = commonData.getDffTime();
        double dffOil = commonData.getDffOil();
        double dffDistance = commonData.getDffDistance();

        //档位校准
        int preGear = var.getPreGear();
        int holdCount = var.getHoldCount();
        if (preGear!=curGear && holdCount<=gearInterfereCount) {
            curGear = preGear;
        }

        VarCommonNumber varGear;
        switch (curGear) {
            case 0: varGear = var.getGear01(); break;
            case 1: varGear = var.getGear02(); break;
            case 2: varGear = var.getGear03(); break;
            case 3: varGear = var.getGear04(); break;
            case 4: varGear = var.getGear05(); break;
            case 5: varGear = var.getGear06(); break;
            case 6: varGear = var.getGear07(); break;
            case 7: varGear = var.getGear08(); break;
            case 8: varGear = var.getGear09(); break;
            case 9: varGear = var.getGear10(); break;
            case 10: varGear = var.getGear11(); break;
            case 11: varGear = var.getGear12(); break;
            case 12: varGear = var.getGear13(); break;
            case 13: varGear = var.getGear14(); break;
            case 14: varGear = var.getGear15(); break;
            case 15: varGear = var.getGear16(); break;
            case 99: varGear = var.getGearOther(); break;
            default: varGear = var.getGearOther(); break;
        }

        if(null!= varGear){
            //如果当前挡位不等于上一点挡位
            if (preGear!=curGear && holdCount>gearInterfereCount) {
                varGear.addNumber(1);
            }
            varGear.addMileage(dffDistance);
            varGear.addFuel(dffOil);
            varGear.addDuration(dffTime);
        }

        calcGearEvent(curGear);
    }

    /**
     * 计算换挡事件的指标
     * @param curGear  当前档位
     */
    private void calcGearEvent(int curGear) {
        VarGear var = getObj();
        CommonData commonData = getCommonData();

        //如果当前挡位不等于上一点挡位
        if (var.getPreGear()!=-1 && var.getPreGear()!=curGear &&  var.getHoldCount()> gearInterfereCount) {
            var.setShiftGearNumber(var.getShiftGearNumber() + 1);

            OuterEventData eventData = new OuterEventData();
            eventData.setTid(Long.valueOf(commonData.getTerminalId()));
            eventData.setDuration((long) commonData.getDffTime());
            eventData.setEvent(EventDataPb.EventType.shiftGears);
            eventData.setStartGpsTime(var.getPreGpsTime());
            eventData.setStartLatitude(var.getPreLatitude());
            eventData.setStartLongitude(var.getPreLongitude());
            eventData.setStartHeight(var.getPreHeight());
            eventData.setStartSpeed(var.getPreSpeedKmh());
            eventData.setStartGpsDirection(var.getPreGpsDirection());
            eventData.setStopGpsTime(commonData.getGpsTime());
            eventData.setStopLatitude(commonData.getLatitude());
            eventData.setStopLongitude(commonData.getLongitude());
            eventData.setStopHeight(commonData.getHeight());
            eventData.setStopSpeed(commonData.getSpeedKmh());
            eventData.setStopGpsDirection(commonData.getGpsDirection());
            eventData.setExtendField01(var.getPreGear() + 1);
            eventData.setExtendField02(curGear + 1);
            eventData.setExtendField03(var.getPreRotation());
            drivingData.addEventDataToList(eventData);
        }
    }

}
